package constructdata;

import codetree.*;

import constructdata.TestDataConstructors.FineTestDataConstructor;
import constructdata.TestDataConstructors.PreTestDataConstructor;
import constructdata.TestDataConstructors.TerminationTestDataConstructor;
import japa.parser.JavaParser;
import japa.parser.ast.stmt.*;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.*;
import japa.parser.ast.Node;
import japa.parser.ast.expr.VariableDeclarationExpr;
import treeview.TreeView;
import treeview.DisplayTreeView;

import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;


/**
 * Created by chen chi on 18/2/7.
 */
public class ConstructTestingData {
    public int linesCount = 0;
    public int test = 0;

    public void constructTestingData(int count, String filePath, boolean isFilePath, List<String> jdkList, FileWriter treeWriter, FileWriter predictionWriter, FileWriter classWriter,
                              FileWriter generationNodeWriter, FileWriter treeSentenceWriter, FileWriter jarWriter, FileWriter holeSizeWriter,
                              FileWriter traceWriter, FileWriter blockpredictionWriter,FileWriter originalStatementsWriter,FileWriter variableNamesWriter, FileWriter linesWriter, FileWriter testCaseTraceWriter, FileWriter blockOriginalStatementWriter,String testcaseDir,boolean holeFlag, String globalPath,
                              List<String> gloveVocabList, List<String> stopWordsList) {
        JapaAst japaAst = new JapaAst(true);
        List<String> tempList = new ArrayList<>();
        CompilationUnit cu = new CompilationUnit();
        try {
            if (isFilePath) {
                cu = JavaParser.parse(new File(filePath));
            } else {
                InputStream in = new ByteArrayInputStream(filePath.getBytes());
                cu = JavaParser.parse(in);
            }
            //tempList = jdtAst.parse(filePath, isFilePath);
            tempList = japaAst.parse(cu);
        } catch (Exception e) {

        } catch (Error e) {

        }
        //如果Import的包中带有*号，那么得到含有*号的这个import
        List importList = cu.getImports();
        List<String> starImportStringList = new ArrayList<>();
        String importInfo = "";
        if (importList != null) {
            for (int i = 0; i < importList.size(); i++) {
                importInfo += importList.get(i).toString();
                if (importList.get(i).toString().contains("*")) {
                    String str = importList.get(i).toString();
                    int index = str.indexOf("import");
                    str = str.substring(index);
                    String[] strs = str.split(" ");
                    str = strs[strs.length - 1];//得到Import的包的信息
                    str = str.replace(" ", ""); //替换掉空格" "
                    str = str.replace(";", ""); //去除;
                    starImportStringList.add(str);
                }
            }
        }
        //开始分析程序
        if (cu.getTypes() != null) {
            for (TypeDeclaration type : cu.getTypes()) {
                if (type instanceof ClassOrInterfaceDeclaration) {
                    String classInfo = "public class "+ ((ClassOrInterfaceDeclaration)type).getName() + "{\r\n" ;
                    String fieldInfo = "";
                    //处理field
                    List<VariableDeclarationExpr> fieldExpressionList = new ArrayList<>();
                    for (BodyDeclaration body : type.getMembers()) {
                        if (body instanceof FieldDeclaration) {
                            FieldDeclaration field = (FieldDeclaration) body;
                            fieldInfo += field.toString();
                            fieldInfo += "\r\n";
                            for (int i = 0; i < field.getVariables().size(); i++) {
                                VariableDeclarationExpr expr = new VariableDeclarationExpr();
                                List list = new ArrayList();
                                list.add(field.getVariables().get(i));
                                expr.setType(field.getType());
                                expr.setVars(list);
                                fieldExpressionList.add(expr);
                            }
                        }
                    }
                    //处理method
                    for (BodyDeclaration body : type.getMembers()) {
                        if (body instanceof MethodDeclaration) {
                            int lines = countCodeLine(body);
                            if (lines >= 2) {
                                List<String> completeClassNameList = new ArrayList<>();
                                for (String str : tempList) {
                                    completeClassNameList.add(str);
                                }
                                List userClassList = new ArrayList();
                                for (String str : japaAst.getFilternames()) {
                                    userClassList.add(str);
                                }
                                UserClassProcessing userClassProcessing = new UserClassProcessing();
                                userClassProcessing.setUserClassList(userClassList);
                                userClassProcessing.setJdkList(jdkList);
                                userClassList.add("userDefinedClass");
                                MethodDeclaration method = (MethodDeclaration) body;
                                //System.out.println("---------------------------------------------");
                                //System.out.println(count + ": " + method.getName() + (method.getParameters() == null ? "[]" : method.getParameters()) + " (" + filePath + ") ");
                                //System.out.println("---------------------------------------------");
                                List<String> parameterNameList = new ArrayList<>();
                                List<String> typeMapList = new ArrayList<>();
                                List<String> completeTypeMapList = new ArrayList<>();
                                List<ExpressionStmt> parameterExpressionList = new ArrayList<>();
                                if (method.getParameters() != null) {
                                    List<Parameter> parameterList = method.getParameters();
                                    for (int i = 0; i < parameterList.size(); i++) {
                                        String contentString = "public class Test{public void test(){$}}";
                                        String parameterString = parameterList.get(i).toString() + ";";
                                        contentString = contentString.replaceAll("\\$", parameterString);
                                        InputStream in = new ByteArrayInputStream(contentString.getBytes());
                                        try {
                                            CompilationUnit compilationUnit = JavaParser.parse(in);
                                            Node node = compilationUnit.getTypes().get(0).getMembers().get(0);
                                            ExpressionStmt expression = (ExpressionStmt) node.getChildrenNodes().get(1).getChildrenNodes().get(0);
                                            parameterExpressionList.add(expression);
                                        } catch (Exception e) {
                                            continue;
                                        } catch (Error e) {
                                            continue;
                                        }
                                        // String[] strings = parameterList.get(i).toString().split(" ");
                                        // parameterNameList.add(strings[strings.length - 1]);
                                        // typeMapList.add(strings[strings.length - 1] + " " + parameterList.get(i).getType().toString());
                                        //completeTypeMapList.add(parameterList.get(i).getType().toString());
                                    }
                                }
                    /*添加类中的成员变量*/
                                SimplifiedTreeCreator creator = new SimplifiedTreeCreator(globalPath);
                                creator.setUserClassProcessing(userClassProcessing);
                                creator.setStarImportStringList(starImportStringList);
                                List<String> tempUserClassList = new ArrayList<>();
                                for (int i = 0; i < completeClassNameList.size(); i++) {
                                    try {
                                        Class clazz = Thread.currentThread().getContextClassLoader().loadClass(completeClassNameList.get(i));
                                        if (jdkList.contains(completeClassNameList.get(i))) {
                                            creator.getClass_name_map().put(clazz.getSimpleName(), completeClassNameList.get(i));
                                        } else {
                                            tempUserClassList.add(completeClassNameList.get(i));
                                            userClassList.add(completeClassNameList.get(i));
                                        }
                                    } catch (Exception e) {
                                        tempUserClassList.add(completeClassNameList.get(i));
                                        userClassList.add(completeClassNameList.get(i));
                                    } catch (Error e) {
                                        //System.err.println(e.getCause());
                                        tempUserClassList.add(completeClassNameList.get(i));
                                        userClassList.add(completeClassNameList.get(i));
                                    }

                                }
                                //过滤掉反射不到的类
                                for (int i = 0; i < tempUserClassList.size(); i++) {
                                    completeClassNameList.remove(tempUserClassList.get(i));
                                }
                                tempUserClassList.removeAll(tempUserClassList);
                                //处理field
                                for (int i = 0; i < fieldExpressionList.size(); i++) {
                                    creator.convert(fieldExpressionList.get(i));
                                }
                                //处理method中的parameter
                                for (int i = 0; i < parameterExpressionList.size(); i++) {
                                    creator.convert(parameterExpressionList.get(i));
                                }
                    /*get code tree from japa parse*/
                                CodeTree codeTree = constructTreeFromAST(completeClassNameList, parameterNameList, typeMapList,
                                        completeTypeMapList, starImportStringList, method, creator, userClassProcessing, holeFlag, globalPath, jdkList,
                                        gloveVocabList,stopWordsList);
                                if (codeTree != null && codeTree.getRoot() != null && codeTree.getTotalNumber() <= 1574) {
                    /*display the code tree*/
                                    //String functionTrace = method.getName() + (method.getParameters() == null ? "[]" : method.getParameters()) + " (" + filePath + ") ";
                                    String functionTrace = method.getName();
                                    functionTrace += "[";
                                    int parameterCount = 0;
                                    if (method.getParameters() != null) {
                                        for (Parameter parameter : method.getParameters()) {
                                            parameterCount++;
                                            if (parameterCount > 1) {
                                                functionTrace += ", ";
                                            }
                                            functionTrace += parameter.getType().toString();
                                            functionTrace += " " + parameter.getId().getName();
                                        }
                                    }
                                    functionTrace += "]";
                                    functionTrace += " (" + filePath + ") ";
                                    functionTrace = functionTrace.replaceAll("\r", "");
                                    functionTrace = functionTrace.replaceAll("\n", "");
                                    codeTree.setFunctionTrace(functionTrace);
                                    codeTree.setSourceCodeTrace(filePath);
                                    codeTree.setSourceInfo(importInfo + classInfo + fieldInfo);
                                    codeTree.setMethodInfo(method.getBeginLine() + " " + method.getEndLine());
                                    linesCount += lines;
//                                    displayTree(codeTree, true, method.getName() + (method.getParameters() == null ? "[]" : method.getParameters()));
//                                    try {
//                                       //TreeWriter.print(codeTree.getRoot(), "graph/2.dot");
//                                    }catch(Exception e){
//                                        e.printStackTrace();
//                                    }
                                    //displayTree(codeTree,false);
                    /*store the code tree in mongodb*/
                                    //storeTreeInDB(codeTree);
                    /*construct training tree data */
                                    constructTestingData(codeTree, treeWriter, predictionWriter, classWriter, generationNodeWriter, treeSentenceWriter, jarWriter, holeSizeWriter, traceWriter, blockpredictionWriter,originalStatementsWriter,variableNamesWriter, linesWriter,testCaseTraceWriter,blockOriginalStatementWriter,testcaseDir,true);
                                } else {
                                    //System.err.println("So " + method.getName() + (method.getParameters() == null ? "[]" : method.getParameters()) + " (" + filePath + ") " + " can not be correctly parsed");
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public CodeTree constructTreeFromAST(List<String> completeClassNameList, List<String> parameterNameList,
                                         List<String> typeMapList, List<String> completeTypeMapList,
                                         List<String> starImportStringList, MethodDeclaration method,
                                         SimplifiedTreeCreator fieldCreator, UserClassProcessing userClassProcessing,
                                         boolean holeFlag, String globalPath, List<String> jdkList,
                                         List<String> gloveVocabList, List<String> stopWordsList ) {
        try {
            SimplifiedTreeCreator creator = new SimplifiedTreeCreator(completeClassNameList, fieldCreator, globalPath, jdkList);
            creator.setHoleFlag(holeFlag);
            for (int i = 0; i < parameterNameList.size(); i++) {
                creator.addClass_variable_list(parameterNameList.get(i));
            }
            for (int i = 0; i < typeMapList.size(); i++) {
                String[] strings = typeMapList.get(i).split(" ");
                creator.addClass_variable(strings[0], strings[1]);
            }
            for (int i = 0; i < completeTypeMapList.size(); i++) {
                creator.addClass_name_map(completeTypeMapList.get(i));
            }
            creator.setStarImportStringList(starImportStringList);
            creator.setUserClassProcessing(userClassProcessing);
            creator.toCodeTree(method);
            CodeTree codeTree = new CodeTree();
            codeTree.setRoot(creator.getCodeTree().getRoot());
            if(creator.getParsedFlag() && codeTree.getRoot() != null) {
                //移除没有用到的变量声明结点
                Map<String, List<TreeNode>> map = creator.getVariableNodeMap();
                for (String key : map.keySet()) {
                    List<TreeNode> list = map.get(key);
                    for (int i = 0; i < list.size(); i++) {
                        if ((list.get(i).isVariableDeclaration() && list.get(i).isPrimitive() && !list.get(i).isVariablePreserved())
                                || list.get(i).isAssign()) {
                            if (!codeTree.removeNode(list.get(i))) {
                                return null;
                            }
                        }
                    }
                }
                //添加类属性变量和函数声明中的变量
                codeTree.getRoot().setPreviousVariableNames(creator.getUsedClassFieldAndMethodArgumentVariable());
                if (codeTree.getRoot().getPreviousVariableNames() != null) {
                    for (int i = 0; i < codeTree.getRoot().getPreviousVariableNames().size(); i++) {
                        String variableName = codeTree.getRoot().getPreviousVariableNames().get(i);
                        variableName = variableName.replaceAll("\r", "");
                        variableName = variableName.replaceAll("\n", "");
                        codeTree.getRoot().getPreviousVariableNames().set(i, variableName);
                    }
                }
                //加入变量名
                codeTree.processRootVariables(codeTree.getRoot(), gloveVocabList, stopWordsList);
                List<String> duplicatedVariableList = new ArrayList<>();
                codeTree.initPreviousVariables(codeTree.getRoot(), duplicatedVariableList, gloveVocabList, stopWordsList);
                // codeTree.dealHoleParentNode(codeTree.getRoot());
                //加入注释
                //codeTree.initCommentList(codeTree.getRoot(), creator.getCommentList());
//                for(int i = 0; i < creator.getCommentList().size(); i ++){
//                    System.out.println(creator.getCommentList().get(i));
//                }
                //过滤掉原始语句中的\r\n符号
                codeTree.filterSpecialCharacterInOriginalStatement(codeTree.getRoot());
                return codeTree;
            }
            else {
                return null;
            }
        } catch (Exception e) {
            //e.printStackTrace();
            return null;
        } catch (Error e) {
            //e.printStackTrace();
            return null;
        }
    }

    public void displayTree(CodeTree codeTree, boolean isCompleteFlag, String title) {
        TreeView treeView = new TreeView();
        treeView.convertCodeTree(codeTree, isCompleteFlag);
        DisplayTreeView display = new DisplayTreeView(treeView.getTree(), title);
    }

    public void storeTreeInDB(CodeTree codeTree) throws IOException {
        CodeTreeOperation codeTreeOperation = new CodeTreeOperation();
        TreeDB db = new TreeDB();
        db.store(codeTreeOperation.setAllParentNodeToNull(codeTree), "codetree", "codetree");
    }


    public void constructTestingData(CodeTree codeTree, FileWriter treeWriter, FileWriter predictionWriter, FileWriter classWriter, FileWriter generationNodeWriter, FileWriter treeSentenceWriter, FileWriter jarWriter, FileWriter holeSizeWriter,
                                      FileWriter traceWriter, FileWriter blockpredictionWriter, FileWriter originalStatementsWriter, FileWriter variableNamesWriter, FileWriter linesWriter,  FileWriter testCaseTraceWriter,FileWriter blockOriginalStatementWriter,
                                     String testcaseDir,boolean isCompleteFlag) {

//         Construct FineTrainingData
        FineTestDataConstructor constructFineTestingData = new FineTestDataConstructor();
        constructFineTestingData.construct(codeTree, treeWriter, predictionWriter, classWriter, generationNodeWriter, treeSentenceWriter, jarWriter, holeSizeWriter, traceWriter, blockpredictionWriter,originalStatementsWriter,variableNamesWriter, linesWriter,testCaseTraceWriter,blockOriginalStatementWriter,testcaseDir,isCompleteFlag);

        // Construct TerminationData
        PreTestDataConstructor constructPreTestingData = new PreTestDataConstructor();
        constructPreTestingData.construct(codeTree, treeWriter, predictionWriter, classWriter, generationNodeWriter, treeSentenceWriter, jarWriter, holeSizeWriter, traceWriter, blockpredictionWriter, originalStatementsWriter,variableNamesWriter,linesWriter,testCaseTraceWriter,blockOriginalStatementWriter,testcaseDir,isCompleteFlag);

        // Construct PreTrainData
        TerminationTestDataConstructor constructTerminationTestingData = new TerminationTestDataConstructor();
        constructTerminationTestingData.construct(codeTree, treeWriter, predictionWriter, classWriter, generationNodeWriter, treeSentenceWriter, jarWriter, holeSizeWriter, traceWriter, blockpredictionWriter, originalStatementsWriter,variableNamesWriter,linesWriter,testCaseTraceWriter,blockOriginalStatementWriter,testcaseDir,isCompleteFlag);

    }

    public int countCodeLine(Node node) {
        int result = 0;
        if (node instanceof Statement && !(node instanceof BlockStmt)) {
            result += 1;
            if(node instanceof IfStmt){
                if(((IfStmt) node).getElseStmt() != null && !(((IfStmt) node).getElseStmt() instanceof IfStmt)){
                    result += 1;
                }
            }else if(node instanceof TryStmt){
                if(((TryStmt) node).getFinallyBlock() != null){
                    result += 1;
                }
            }
        }else if(node instanceof CatchClause){
            result += 1;
        }
        if (node.getChildrenNodes() != null) {
            for (int i = 0; i < node.getChildrenNodes().size(); i++) {
                result += countCodeLine(node.getChildrenNodes().get(i));
            }
        }
        return result;
    }

}
